
<!DOCTYPE html>
<html lang="en">
    <head>

    <title>CLR重定向 — ILRuntime</title>
    <meta charset="utf-8">
    <meta name="description" content="ILRuntime">
    <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">

    <link rel="stylesheet" href="/ILRuntime/public/css/page.css">

    <script src="/ILRuntime/public/js/vue.js"></script>
    <script src="/ILRuntime/public/js/jquery.js"></script>
    </head>

    <body>
        
            <nav class="nav">
    <div class="border">
        <img src="/ILRuntime/public/images/logo.png" />
        <button class="hiden-in-phone">V1.4</button>
        <button id="btn-menu" class="hiden-in-pc">菜单</button>
        <ul class="nav-link hiden-in-phone">
            <!--li>
                <form id="search-form">
                    <input type="text" id="search-query" class="search-query">
                </form>
            </li!-->
            <li><a href="https://github.com/Ourpalm/ILRuntime" class="nav-link-li">下载项目</a></li>
            <li><a href="/ILRuntime/public/v1/guide/version.html" class="nav-link-li">更新记录</a></li>
            <li><a href="/ILRuntime/public/v1/guide/contribution.html" class="nav-link-li">贡献指南</li>
            <li><a href="/ILRuntime/public/v1/guide/index.html" class="nav-link-li">教程</a></li>
            <li><a href="/ILRuntime/public/" class="nav-link-li current">首页</a></li>
        </ul>
    </div>
</nav>

<div id="container" class="container clear">
    <section class="sidebar clearfix">
    <ul>
        
            
            
                <li><h3>基础</h3></li>
            
            
            
            <li>
                <p><a href="/ILRuntime/public/v1/guide/index.html" class="sidebar-link">介绍</a></p>
            </li>
        
            
            
            
            
            <li>
                <p><a href="/ILRuntime/public/v1/guide/version.html" class="sidebar-link">更新记录</a></p>
            </li>
        
            
            
            
            
            <li>
                <p><a href="/ILRuntime/public/v1/guide/contribution.html" class="sidebar-link">贡献指南</a></p>
            </li>
        
            
            
            
                <li><h3>教程</h3></li>
            
            
            <li>
                <p><a href="/ILRuntime/public/v1/guide/tutorial.html" class="sidebar-link">从零开始</a></p>
            </li>
        
            
            
            
            
            <li>
                <p><a href="/ILRuntime/public/v1/guide/delegate.html" class="sidebar-link">ILRuntime中使用委托</a></p>
            </li>
        
            
            
            
            
            <li>
                <p><a href="/ILRuntime/public/v1/guide/cross-domain.html" class="sidebar-link">ILRuntime中跨域继承</a></p>
            </li>
        
            
            
            
            
            <li>
                <p><a href="/ILRuntime/public/v1/guide/reflection.html" class="sidebar-link">ILRuntime中的反射</a></p>
            </li>
        
            
            
            
            
            <li>
                <p><a href="/ILRuntime/public/v1/guide/redirection.html" class="sidebar-link current">CLR重定向</a></p>
            </li>
        
            
            
            
            
            <li>
                <p><a href="/ILRuntime/public/v1/guide/bind.html" class="sidebar-link">CLR绑定</a></p>
            </li>
        
            
            
            
            
            <li>
                <p><a href="/ILRuntime/public/v1/guide/litjson.html" class="sidebar-link">LitJson集成</a></p>
            </li>
        
            
            
            
            
                <li><h3>其他</h3></li>
            
            <li>
                <p><a href="/ILRuntime/public/v1/guide/il2cpp.html" class="sidebar-link">IL2CPP打包注意事项</a></p>
            </li>
        
            
            
            
            
            <li>
                <p><a href="/ILRuntime/public/v1/guide/performance-optimization.html" class="sidebar-link">ILRuntime的性能优化</a></p>
            </li>
        
            
            
            
            
            <li>
                <p><a href="/ILRuntime/public/v1/guide/principle.html" class="sidebar-link">ILRuntime的实现原理</a></p>
            </li>
        
    </ul>
</section>
    <article class="clearfix">
    <h2 id="CLR重定向"><a href="#CLR重定向" class="headerlink" title="CLR重定向"></a>CLR重定向</h2><p>在开发中，如ILRuntime的反射那篇文档中说的，一些依赖反射的接口是没有办法直接运行的，最典型的就是在Unity主工程中通过new T()创建热更DLL内类型的实例。<br>细心的朋友一定会好奇，为什么Activator.CreateInstance<type>();这个明显内部是new T();的接口可以直接调用呢？</type></p>
<p>ILRuntime为了解决这类问题，引入了CLR重定向机制。 原理就是当IL解译器发现需要调用某个指定CLR方法时，将实际调用重定向到另外一个方法进行挟持，再在这个方法中对ILRuntime的反射的用法进行处理</p>
<p>刚刚提到的Activator.CreateInstance<t>的CLR重定向定义如下：<br><figure class="highlight csharp"><table><tr><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">static</span> StackObject* CreateInstance(ILIntepreter intp, StackObject* esp, List&lt;<span class="keyword">object</span>&gt; mStack, CLRMethod method, <span class="keyword">bool</span> isNewObj)</span><br><span class="line">&#123;</span><br><span class="line">    <span class="comment">//获取泛型参数&lt;T&gt;的实际类型</span></span><br><span class="line">    IType[] genericArguments = method.GenericArguments;</span><br><span class="line">    <span class="keyword">if</span> (genericArguments != <span class="literal">null</span> &amp;&amp; genericArguments.Length == <span class="number">1</span>)</span><br><span class="line">    &#123;</span><br><span class="line">        <span class="keyword">var</span> t = genericArguments[<span class="number">0</span>];</span><br><span class="line">        <span class="keyword">if</span> (t <span class="keyword">is</span> ILType)<span class="comment">//如果T是热更DLL里的类型</span></span><br><span class="line">        &#123;</span><br><span class="line">            <span class="comment">//通过ILRuntime的接口来创建实例</span></span><br><span class="line">            <span class="keyword">return</span> ILIntepreter.PushObject(esp, mStack, ((ILType)t).Instantiate());</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">else</span></span><br><span class="line">            <span class="keyword">return</span> ILIntepreter.PushObject(esp, mStack, Activator.CreateInstance(t.TypeForCLR));<span class="comment">//通过系统反射接口创建实例</span></span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">else</span></span><br><span class="line">        <span class="keyword">throw</span> <span class="keyword">new</span> EntryPointNotFoundException();</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></t></p>
<p>要让这段代码生效，需要执行相对应的注册方法：<br><figure class="highlight csharp"><table><tr><td class="code"><pre><span class="line"><span class="keyword">foreach</span> (<span class="function"><span class="keyword">var</span> i <span class="keyword">in</span> <span class="title">typeof</span>(<span class="params">System.Activator</span>).<span class="title">GetMethods</span>(<span class="params"></span>))</span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="comment">//找到名字为CreateInstance，并且是泛型方法的方法定义</span></span><br><span class="line">    <span class="keyword">if</span> (i.Name == <span class="string">"CreateInstance"</span> &amp;&amp; i.IsGenericMethodDefinition)</span><br><span class="line">    &#123;</span><br><span class="line">        appdomain.RegisterCLRMethodRedirection(i, CreateInstance);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p>
<h3 id="带参数的方法的重定向"><a href="#带参数的方法的重定向" class="headerlink" title="带参数的方法的重定向"></a>带参数的方法的重定向</h3><p>刚刚的例子当中，由于CreateInstance<t>方法并没有任何参数，所以需要另外一个例子来展示用法，最好的例子就是Unity的Debug.Log接口了，默认情况下，如果在DLL工程中调用该接口，是没有办法显示正确的调用堆栈的，会给开发带来一些麻烦，下面我会展示怎么通过CLR重定向来实现在Debug.Log调用中打印热更DLL中的调用堆栈</t></p>
<figure class="highlight csharp"><table><tr><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">unsafe</span> <span class="keyword">static</span> StackObject* DLog(ILIntepreter __intp, StackObject* __esp, List&lt;<span class="keyword">object</span>&gt; __mStack, CLRMethod __method, <span class="keyword">bool</span> isNewObj)</span><br><span class="line">&#123;</span><br><span class="line">    ILRuntime.Runtime.Enviorment.AppDomain __domain = __intp.AppDomain;</span><br><span class="line">    StackObject* ptr_of_this_method;</span><br><span class="line">    <span class="comment">//只有一个参数，所以返回指针就是当前栈指针ESP - 1</span></span><br><span class="line">    StackObject* __ret = ILIntepreter.Minus(__esp, <span class="number">1</span>);</span><br><span class="line">    <span class="comment">//第一个参数为ESP -1， 第二个参数为ESP - 2，以此类推</span></span><br><span class="line">    ptr_of_this_method = ILIntepreter.Minus(__esp, <span class="number">1</span>);</span><br><span class="line">    <span class="comment">//获取参数message的值</span></span><br><span class="line">    <span class="keyword">object</span> message = StackObject.ToObject(ptr_of_this_method, __domain, __mStack);</span><br><span class="line">    <span class="comment">//需要清理堆栈</span></span><br><span class="line">    __intp.Free(ptr_of_this_method);</span><br><span class="line">    <span class="comment">//如果参数类型是基础类型，例如int，可以直接通过int param = ptr_of_this_method-&gt;Value获取值，</span></span><br><span class="line">    <span class="comment">//关于具体原理和其他基础类型如何获取，请参考ILRuntime实现原理的文档。</span></span><br><span class="line">			</span><br><span class="line">    <span class="comment">//通过ILRuntime的Debug接口获取调用热更DLL的堆栈</span></span><br><span class="line">    <span class="keyword">string</span> stackTrace = __domain.DebugService.GetStackTrance(__intp);</span><br><span class="line">    Debug.Log(<span class="keyword">string</span>.Format(<span class="string">"&#123;0&#125;\n&#123;1&#125;"</span>, format, stackTrace));</span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> __ret;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>然后在通过下面的代码注册重定向即可：<br><figure class="highlight csharp"><table><tr><td class="code"><pre><span class="line">appdomain.RegisterCLRMethodRedirection(<span class="keyword">typeof</span>(Debug).GetMethod(<span class="string">"Log"</span>), DLog);</span><br></pre></td></tr></table></figure></p>

    <div class="footer">
        发现错误？想参与编辑？ 
        <a href="https://github.com/Ourpalm/ILRuntime/tree/master/docs/source/src/v1/guide/redirection.md" target="_blank">
            在 Github 上编辑此页！
        </a>
    </div>
</article>

<div class="sub-nav hiden-in-phone">
    <dl id="sub-nav">
        <dt>本文内容</dt>
        <dd v-for="(ele, index) in sub_nav">
           <a v-bind:href="ele.href">{{ ele.name }}</a>
        </dd>
    </dl>
</div> 
</div>

<footer>
    <div>
        <p>© Copyright 2017 Ourpalm All Rights Reserved</p>
        <p>掌趣科技2017</p>
    </div>
</footer>


<script>
var vm = new Vue({
    el : '#container',
    data: {
        sub_nav : [ ]
    },
    created:function(){
        var obj = [];
        $("article h3").each(function(){
            obj.push({name :  $(this).find("a").attr("title") , href : "#"+$(this).attr("id") });
        });

        this.sub_nav = obj;       
    }
});

var isShow = false;
$("nav").on("click","#btn-menu" , function(){

    if(!isShow){
        
        if($(document).scrollTop() > $(".sidebar").height() - 100){

            $('html, body').animate({scrollTop:0} , 300, "swing",function(){

                $(".sidebar").fadeIn();
                $(".container").animate({"left" : "15rem"}, 500,"swing");
            });
        }else{

                $(".sidebar").fadeIn();
                $(".container").animate({"left" : "15rem"}, 500,"swing");

        }

    }else{
        $(".sidebar").fadeOut();
        $(".container").animate({"left" : "0rem"}, 500,"swing");
    }
    isShow = !isShow;

});

$(".container").on("click" , "article" , function(){

    if(isShow){
        $(".sidebar").fadeOut();
        $(".container").animate({"left" : "0rem"}, 500,"swing");
        isShow = false;    
    }

});
</script>
        
    </body>
</html>